iOS GCD 之 dispatch_group

问题的产生

在开发过程中有时会遇到这样的需求:并发执行一组任务,任务全部结束后,对执行结果进行某些操作。

比如类似下面这样。

1
2
3
4
for (id obj in array) {
[self doSomethingIntensiveWith:obj];
}
[self doSomethingWithArray:array];

直接使用以上代码会导致阻塞,等待任务组全部都执行完才执行 doSomethingWithArray: 。

简单地加入 dispatch_async 也是不行的,这样 doSomethingWithArray: 不能在正确的时机运行。

不使用 dispatch_group 的解决方案

在知道 dispatch_group 之前 ,我的解决方法是加入一个原子计数器,等所有任务都执行完了,再执行结果列表的操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
__block volatile int32_t remainCount = (int32_t)array.count;
void (^checkRemainCountBlock)(void) = ^(void) {
if (remainCount == 0) {
[self doSomeThingWithArray:array];
}
OSAtomicDecrement32Barrier(&remainCount);
};
checkRemainCountBlock();

for (id obj in array) {
[self doSomethingIntensiveWith:obj];
checkRemainCountBlock();
}

使用 dispatch_group 的解决方案

可以看到,上面的方法不太优雅。事实上,Apple 为我们准备了专门用来处理这种问题的 dispatch_group。

一个 dispatch group 可以用来将多个 block 组成一组以监测这些 block 全部完成或者等待全部完成时发出的消息。所以我们现在可以重写代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
dispatch_queue_t queue = dispatch_get_global_qeueue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
for(id obj in array) {
dispatch_group_async(group, queue, ^{
[self doSomethingIntensiveWith:obj];
});
}

/*
* 等group里的task都执行完后执行notify方法里的内容,
* 相当于把wait方法及之后要执行的代码合到一起了
*/

dispatch_group_notify(group, queue, ^{
[self doSomethingWithArray:array];
});

在不能使用 dispatch_group_async 的情况下,使用 dispatch_group_enter/dispatch_group_leave。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
dispatch_queue_t queue = dispatch_get_global_qeueue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
for(id obj in array) {
dispatch_group_enter(group);
dispatch_async(queue, ^{
[self doSomethingIntensiveWith:obj];
dispatch_group_leave(group);
});
}

/*
* 等group里的task都执行完后执行notify方法里的内容,
* 相当于把wait方法及之后要执行的代码合到一起了
*/

dispatch_group_notify(group, queue, ^{
[self doSomethingWithArray:array];
});

dispatch_group 相关方法

  • dispatch_group_create
    创建一个调度任务组

  • dispatch_group_async
    把一个任务异步提交到任务组里

  • dispatch_group_enter/dispatch_group_leave
    这两个方法显示的讲任务组中的任务未执行完毕的任务数目加减1,这种方式用在不使用 dispatch_group_async 提交任务的情况下。注意:这两个函数要配合使用,有 enter 要有 leave,这样才能保证功能完整实现。也可以用这对函数来让一个闭包关联多个 group

  • dispatch_group_notify
    用来监听任务组事件的执行完毕

  • dispatch_group_wait
    设置等待时间,在等待时间结束后,如果还没有执行完任务组,则返回。返回0代表执行成功,非0则执行失败